﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.WPF.Custom;
using Bling.Shapes;
using Bling.Core;
using Bling.Util;
using Bling.Charts;
using Bling.Angles;

/// hello world.
namespace Bling.Tutorial {
  public class Pie3DBl : CanvasBl<Pie3D, Pie3DBl> {
    public Pie3DBl(Expr<Pie3D> v) : base(v) { }
    public Pie3DBl() : base() { }
    /// <summary>
    /// Create a new pie with specified slices.
    /// </summary>
    /// <param name="Parent">Canvas that contains this pie.</param>
    /// <param name="Slices">Slices of the pie, will be used to sequentially create pie pieces.</param>
    public Pie3DBl(CanvasBl Parent, params DoubleBl[] Slices) : base(Parent, new Pie3D(Slices)) { }
    public static implicit operator Pie3DBl(Expr<Pie3D> v) { return new Pie3DBl(v); }
    public static implicit operator Pie3DBl(Pie3D v) { return new Constant<Pie3D>(v); }
    public PieBl Pie {
      get { return CurrentValue.Pie; } 
    }
    public DoubleBl SideHeight { get { return Pie3D.SideHeightProperty[Provider]; } set { SideHeight.Bind = value; } }
    public DoubleBl Squash { get { return Pie3D.SquashProperty[Provider]; } set { Squash.Bind = value; } }
    public DoubleBl Radius {
      get { return Pie3D.RadiusProperty[Provider]; }
      set { Radius.Bind = value; } 
    }
  }
  public class Pie3D : Canvas {
    public readonly Pie Pie;
    public static readonly GetProperty<Pie3D,double> RadiusProperty = "Radius".NewProperty<Pie3D, double>(100d);
    public static readonly GetProperty<Pie3D, double> SideHeightProperty = "SideHeight".NewProperty<Pie3D, double>(50d);
    public static readonly GetProperty<Pie3D, double> SquashProperty = "Squash".NewProperty<Pie3D, double>(.7d);

    public Pie3D(params DoubleBl[] Slices) {
      Pie3DBl Self = (this);
      DoubleBl ScaledSideHeight = Self.SideHeight * (1d - Self.Squash);

      var PieCanvas = new CanvasBl(Self) {
        Size = new Double2Bl(2d, 2d) * Self.Radius + new Double2Bl(0, ScaledSideHeight),
      };
      this.Pie = new PieBl(PieCanvas, Slices) {
        Size = new Double2Bl(2d, 2d) * Self.Radius, LeftTop = new Double2Bl(0, 0),
        ZIndex = 2, // 3D effect will be below this.
        Opacity = 1,
      };

      var side = new CanvasBl(PieCanvas) {
        ZIndex = 1, Size = PieCanvas.Size, LeftTop = new Double2Bl(0, 0),
        Clip = GeometryBl.Ellipse(Self.Pie.CenterPosition + new Double2Bl(0, ScaledSideHeight), new Double2Bl(Self.Radius, Self.Radius)) 
      };
      var side2 = new CanvasBl(PieCanvas) {
        ZIndex = 1, Size = PieCanvas.Size, LeftTop = new Double2Bl(0, 0),
        Clip = GeometryBl.Rectangle(new Double2Bl(0, Self.Pie.Height * 0.5), new Double2Bl(Self.Radius * 2, ScaledSideHeight))
      };
      var fillColor = new LinearGradientBrushBl() { StartPoint = new Double2Bl(0, 1), EndPoint = new Double2Bl(1, 1) };
      fillColor.Stop(Colors.Black, 0.01).Stop(Colors.Gray, 0.5).Stop(Colors.Black, 0.99);
      var sideX = new RectangleBl(side) {
        Size = side.Size, LeftTop = side.LeftTop, Opacity = 0.45,
        Fill = fillColor, ZIndex = 1,
      };
      var sideX2 = new RectangleBl(side2) {
        Size = side2.Size, LeftTop = side2.LeftTop, Opacity = 0.45,
        Fill = fillColor, ZIndex = 1,
      };
      PieCanvas.RenderTransform[new Double2Bl(0, 0)].Scale = new Double2Bl(1, Self.Squash);
      var ActualPieCanvasSize = PieCanvas.Size * new Double2Bl(1, Self.Squash);
      Self.Size = ActualPieCanvasSize + new Double2Bl(100, 30 + 100 * Self.Squash);
      //PieCanvas.CenterPosition = Self.CenterSize;
      PieCanvas.LeftTop = Self.CenterSize - ActualPieCanvasSize / 2d;

      var specialLeftTop = (Self.Pie.CenterSize + .5.PI().ToPoint * 200);


      for (int i = 0; i < Slices.Length; i++) {
        var labelAngle = (Self.Pie.Rotation(i) + Self.Pie[i].WedgeAngle / 2d).Normalize;
        var ratio = (labelAngle - 1.PI()).PI.Abs;
        var labelPos =
          (new Double2Bl(1d, 1d) * Self.Radius * new Double2Bl(1, Self.Squash) + PieCanvas.LeftTop)
          + (labelAngle.ToPoint * (Self.Radius + 20d + ratio * 20d)) * new Double2Bl(1, Self.Squash)
          ;
        var labelContent = (Slices[i] / Pie.Total * 100d).Round;
        var label = new LabelBl(Self) {
          ZIndex = 10,
          CenterPosition = labelPos, Content = labelContent.ToStringBl() + "%",
          Font = { Size = 15 + 5 * ratio, Weight = FontWeights.SemiBold, Brush = { Bind = Self.Pie[i].Fill } },
          Visibility = labelContent > 0,
        };
        var startA = Pie.Rotation(i);
        var endA = Pie.Rotation((i + 1) % Slices.Length);



        var nextLeftTop = Self.CenterSize + (endA.Clamp(0d.PI(), 1d.PI()).ToPoint * (Self.Radius));
        var atLeftTop = Self.CenterSize + (startA.Clamp(0d.PI(), 1d.PI()).ToPoint * (Self.Radius));
        var leftTop = nextLeftTop.Min(atLeftTop);
        var rightBottom = nextLeftTop.Max(atLeftTop);
        var size = rightBottom - leftTop;
        var IsVisible = (startA > 0d.PI() & startA < 1d.PI()) | (endA > 0d.PI() & endA < 1d.PI());
        var width = size.X;
        var left = leftTop.X;
        if (true) {
          if (true) new RectangleBl(side) {
            Fill = Self.Pie[i].Fill,
            Stroke = Self.Pie[i].Stroke,
            LeftTop = new Double2Bl(leftTop.X - side.Left - PieCanvas.Left, 0),
            Size = new Double2Bl(size.X, side.Height),
            Visibility = IsVisible,
            ZIndex = 0,
          };
          if (true) new RectangleBl(side2) {
            Fill = Self.Pie[i].Fill,
            LeftTop = new Double2Bl(leftTop.X - side2.Left - PieCanvas.Left, 0),
            Size = new Double2Bl(size.X, side2.Height),
            Stroke = Self.Pie[i].Stroke,
            ZIndex = 0,
            Visibility = IsVisible,
          };
        }
      }
    }
  }


  public class PieTest : Canvas {
    /// <summary>
    /// Colors to use in pie chart
    /// </summary>
    private static readonly Color[] MyColors = new Color[] {
      "#c24a2e".Convert<Color>(),
      "#e96749".Convert<Color>(),
      //"#ec9874".Convert<Color>(),
      "#a33255".Convert<Color>(),
      "#cf5872".Convert<Color>(),
      //"#df8398".Convert<Color>(),
      "#385f57".Convert<Color>(),
      "#558577".Convert<Color>(),
      //"#8db29e".Convert<Color>(),
      "#1f72ae".Convert<Color>(),
      "#3188c8".Convert<Color>(),
      //"#63a5d6".Convert<Color>(),
      "#5c397e".Convert<Color>(),
      "#9d61a8".Convert<Color>(),
      //"#ca8acd".Convert<Color>(),
      "#b2712a".Convert<Color>(),
      "#c69746".Convert<Color>(),
      //"#e1be70".Convert<Color>(),
      "#616637".Convert<Color>(),
      "#8c8f4c".Convert<Color>(),
      //"#b6bb7a".Convert<Color>(),
      "#0d7771".Convert<Color>(),
      "#199d8d".Convert<Color>(),
      //"#65bebb".Convert<Color>(),
    };

    public PieTest() { Test1(); }

    public void Test1() {
      CanvasBl MainCanvas = this;
      Random r = new Random();
      var squash = new LabelSliderBl(MainCanvas) {
        Width = MainCanvas.Width, ZIndex = 20, LeftTop = new Double2Bl(0, 0),
        Minimum = .1, Maximum = 1.0, Value = .7, LabelName = "Squash",
      };
      var sideHeight = new LabelSliderBl(MainCanvas) {
        Width = MainCanvas.Width, ZIndex = 20, LeftTop = squash.LeftBottom,
        Minimum = 0, Maximum = 300, Value = 50, LabelName = "Depth",
      };
      var randomize = new ButtonBl(MainCanvas) {
        Content = "Change", LeftTop = sideHeight.LeftBottom,
      };

      var slices = new DoubleBl[10];
      {
        Double2Bl P = new Double2Bl(0, MainCanvas.Height);
        for (int i = 0; i < slices.Length; i++) {
          var slider = new LabelSliderBl(MainCanvas) {
            Minimum = 0, Maximum = 1, Value = { Now = r },
            LeftBottom = P, Width = MainCanvas.Width, WidthRatio = .1, LabelName = "P" + i,
          };
          slices[i] = slider;
          P = slider.LeftTop;
        }
      }
      //randomize.Click = () => {
        for (int i = 0; i < slices.Length; i++) {
          slices[i][randomize.Click1].Animate().Duration(500).Tween(t => t.Ease.Cubic.Out).To = DoubleBl.State(
            () => {
              return r.NextDouble();
            });
        }
      //};
      DoubleBl Radius = 100d;
      var Pie3D = new Pie3DBl(MainCanvas, slices) {
        CenterTop = new Double2Bl(MainCanvas.CenterSize.X, 100d),
        Radius = Radius, SideHeight = sideHeight, Squash = squash,
        ClipToBounds = true, Background = Brushes.LightGray
      };
      HashSet<int> usedColors = new HashSet<int>();
      for (int i = 0; i < slices.Length; i++) {
        var colorIndex = r.Next(0, MyColors.Length);
        while (usedColors.Contains(colorIndex))
          colorIndex = r.Next(0, MyColors.Length);
        usedColors.Add(colorIndex);
        Pie3D.Pie[i].Fill = MyColors[colorIndex];
      }
    }
  }
}